﻿#define PARALLEL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Collections;


namespace OneSchema
{
    public class UniversalBitSet<T> : IEnumerable<T>
    {

        // List of all elements in universal set
        private List<T> m_elements = new List<T>();

        // Mapping of elements to corresponding bit pattern for constant time lookup.
        private Dictionary<T, UInt64> m_BitMap = new Dictionary<T, UInt64>();

        // Constructor
        public UniversalBitSet()
        { }

        public UniversalBitSet(IEnumerable<T> allElements)
        {
            UInt64 i = 1;
            foreach (T s in allElements)
            {
                // Check if element already exists
                if (!m_BitMap.ContainsKey(s))
                {
                    m_BitMap.Add(s, i);
                    m_elements.Add(s);
                }
                i <<= 1;
            }
        }

        public List<T> Elements { get { return m_elements; } }
        
        /// <summary>
        /// Returns the bit pattern corresponding specified item.
        /// </summary>
        public UInt64 BitPattern(T element)
        { 
            return m_BitMap[element]; 
        }

        /// <summary>
        /// Add an item to universal set.
        /// </summary>
        /// <returns>true if item is added; false if item already exists.</returns>
        public bool Add(T element)
        {
            if (m_BitMap.ContainsKey(element))
                return false;

            m_BitMap.Add(element, 1UL << m_elements.Count);
            m_elements.Add(element);
            return true;
        }

        public ConcurrentBag<BitSet<T>> GenerateSubsetList()
        {
            ConcurrentBag<BitSet<T>> retBag = new ConcurrentBag<BitSet<T>>();

#if PARALLEL
            long upperLimitExclusive = 1L << m_elements.Count;
            Parallel.For(1L, upperLimitExclusive, (i) =>
            {
                BitSet<T> newSet = new BitSet<T>(this);
                newSet.BitPattern = (ulong)i;
                retBag.Add(newSet);
            });
#else
            // Upper limit = 2^(m_elements.Count)-1
            UInt64 upperLimit = (1UL << m_elements.Count) - 1 + (1UL << m_elements.Count);
            for (UInt64 i = 1; i <= upperLimit; i++)
            {
                BitSet<T> newSet = new BitSet<T>(this);
                newSet.BitPattern = i;
                retBag.Add(newSet);
            }
#endif
            return retBag;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return m_elements.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}
